\title{Phase Lock Loop Components in myHDL} \author{Steven K Armour} \maketitle

This notebook is an exploration into the building and testing the Phase Lock Detector and the frequency divider components of an all Digital Phase Lock Loop. Here the Digitial Oscillator and the low pass filter are left for there own exploratory analysis to then allow the reader to design and implement there own PLL.

References

@misc{allen_2003, title={LECTURE 170 APPLICATIONS OF PLLS AND FREQUENCY DIVIDERS (PRESCALERS)}, author={Allen, Phillip E.}, year={2003} },

@phdthesis{gal_2012, title={Design of Fractional-N Phase Locked Loops For Frequency Synthesis From 30 To 40 GHz}, school={McGill University}, author={Gal, George}, year={2012} },

@misc{niknejad_2014, title={Phase Locked Loops (PLL) and Frequency Synthesis}, author={Niknejad, Ali M.}, year={2014} },

@book{razavi_2009, place={Upper Saddle River, NJ}, edition={1}, title={RF microelectronics}, publisher={Prentice Hall}, author={Razavi, Behzad}, year={2009}, pages={Chapter 8} }

@book{craninckx_steyaert_1998, place={New York}, title={Wireless CMOS frequency synthesizer design}, publisher={Springer}, author={Craninckx, J and Steyaert, M}, year={1998} pages={42-46} }

Libraries used and aux functions


In [1]:
from myhdl import *
from myhdlpeek import Peeker

In [2]:
#helper  functions to read in the .v and .vhd generated files into python
def VerilogTextReader(loc, printresult=True):
    with open(f'{loc}.v', 'r') as vText:
        VerilogText=vText.read()
    if printresult:
        print(f'***Verilog modual from {loc}.v***\n\n', VerilogText)
    return VerilogText

def VHDLTextReader(loc, printresult=True):
    with open(f'{loc}.vhd', 'r') as vText:
        VerilogText=vText.read()
    if printresult:
        print(f'***VHDL modual from {loc}.vhd***\n\n', VerilogText)
    return VerilogText

The Phase Lock Loop

The phase lock loop (PLL) is one of the Six classical feedback topologies in electrical engineering. The others being the Voltage-Voltage (Series-Shunt), Voltage-Current(Shunt-Shunt), Current-Current (Shunt-Shunt), Current-Voltage (Series-Series), and the Autoregressive Moving Average (ARMA, aka IIR Filter). The PLL differs from the rest in that it is temporal compersion feedback system that only works for oscillatory information. Looking at the diagram below for the generic classical PLL we see that it is made of Five (six if a frequency divider is added to the reference clock) components. Two of which the Phase Detector and the output frequency divider that make up the feedback loop are somewhat unique to the PLL and are the primary concern of this notebook.

The other components that are part of the forward path are the filter that helps take out some of the "phase noise" error that creeps into the PLL and is typical of the Low Pass variety. The reference oscillator that since the PLL controls phase and phase is a measure of temporal displacement against a reference and the Controlled Oscillator. In brief, the controlled oscillator is made to accelerate its temporal progression (how fast it oscillates) relative to the reference oscillator the error provided by the feedback loop. Where the error is then given as

$$e(t)=K_{PD}(\Phi_{\text{ref}}(t)-\Phi_{\text{ocl}}(t)/N)$$

Where

$K_{PD}$ is the transfer function of the Phase Detector that will be expanded upon shortly. the noting that the Transfer Function for the Voltage control oscillator is $K_{CO}$ and that of the filter is $H(s)$ the resulting open loop and closed loop gain can be found to be

$$A(s)=\dfrac{K_{PD} H(s) K_{CO}}{Ns}$$$$G(s)=\dfrac{K_{PD} H(s) K_{CO}}{s+K_{PD} H(s) \dfrac{K_{CO}}{N}}$$

Phase Detectors

The phase detector (PD) is the only must-have component to a PLL that makes a PLL a PLL via calculating the phase offset error from a reference frequency source and the controlled frequency source via the negative feedback loop. It is the job of the PD to ensure that the waveform of the output is within some portion is in sync with the reference waveform thereby creating a phase lock. Where if the reface waveform and the controlled waveform are out of sync then the loop is said to be unlocked. While for analog PLL there are a variety of PDs which are mostly based on mixers (see Wiki Phase detector) for digital Phase Detectors there are basically only two kinds. The very primitive Negated XOR and the DFlipFlop Stat machine variety

Negated XOR

NXOR_PD myHDL implementation


In [3]:
@block
def NXORPD(clkREF, clkFB, LOCK):
    """
    Negated XOR Phase Detector
    I/O:
        clkREF (bool; in): Ref clock
        clkFB (bool; in): Compere clock
        LOCK (bool; out): Negated XOR (LOCK) result
    
    """
    @always_comb
    def logic():
        LOCK.next= not (clkREF^clkFB)
    
    return instances()

NXOR_PD myHDL Testing


In [4]:
#clear peeker and create test signals
Peeker.clear()
clkREF=Signal(bool(0)); Peeker(clkREF, 'clkREF')
clkFB=Signal(bool(0)); Peeker(clkFB, 'clkFB')
LOCK=Signal(bool(0)); Peeker(LOCK, 'LOCK')
#this clk is a Witness clock refrance
clk=Signal(bool(0)); Peeker(clk, 'clk')

#bind the signals to the DUT
DUT=NXORPD(clkREF, clkFB, LOCK)

def NXORPD_TB(RefDelay=2, FBDelay=4):
    """
    Negated XOR Phase Detector Testbench
    Args:
        RefDelay (int; 2): refrance clock delay cyles compared to refrance
        FBDelay (int; 4): feedback clock delay cyles compared to refrance
        
    """
    #witness clock 
    @always(delay(1))
    def clkGen():
        clk.next=not clk
    
    #refrance clock
    @always(delay(RefDelay))
    def RefClkGen():
        clkREF.next=not clkREF
    #feedback clock
    @always(delay(FBDelay))
    def FBClkGen():
        clkFB.next=not clkFB
    
    #run the simulation
    @instance
    def stimulus():
        for i in range(100):
            yield clk.posedge
        
        raise StopSimulation()
    
    return instances()

sim=Simulation(DUT, NXORPD_TB(), *Peeker.instances()).run()

In [5]:
Peeker.to_wavedrom(start_time=0, stop_time=20)



In [6]:
#pull the sim data into a PD dataframe
NXORPDRes=Peeker.to_dataframe()
#reorder the collums
NXORPDRes=NXORPDRes.reindex(columns=['clk', 'clkREF', 'clkFB', 'LOCK']);NXORPDRes
#show the top ten
NXORPDRes.head(10)


Out[6]:
clk clkREF clkFB LOCK
0 0 0 0 1
1 1 0 0 1
2 0 1 0 0
3 1 1 0 0
4 0 0 1 0
5 1 0 1 0
6 0 1 1 1
7 1 1 1 1
8 0 0 0 1
9 1 0 0 1

In [7]:
#review what the Ref and FB clock values where when the PD was locked
NXORPDRes[NXORPDRes['clk']==1][NXORPDRes['LOCK']==1].head(10)


/home/iridium/anaconda3/lib/python3.6/site-packages/ipykernel_launcher.py:2: UserWarning: Boolean Series key will be reindexed to match DataFrame index.
  
Out[7]:
clk clkREF clkFB LOCK
1 1 0 0 1
7 1 1 1 1
9 1 0 0 1
15 1 1 1 1
17 1 0 0 1
23 1 1 1 1
25 1 0 0 1
31 1 1 1 1
33 1 0 0 1
39 1 1 1 1

In [8]:
#review what the Ref and FB clock values where when the PD was unlocked
NXORPDRes[NXORPDRes['clk']==1][NXORPDRes['LOCK']==0].head(10)


/home/iridium/anaconda3/lib/python3.6/site-packages/ipykernel_launcher.py:2: UserWarning: Boolean Series key will be reindexed to match DataFrame index.
  
Out[8]:
clk clkREF clkFB LOCK
3 1 1 0 0
5 1 0 1 0
11 1 1 0 0
13 1 0 1 0
19 1 1 0 0
21 1 0 1 0
27 1 1 0 0
29 1 0 1 0
35 1 1 0 0
37 1 0 1 0

Verilog implementation


In [9]:
DUT.convert()
VerilogTextReader('NXORPD');


***Verilog modual from NXORPD.v***

 // File: NXORPD.v
// Generated by MyHDL 0.10
// Date: Mon May 21 21:38:22 2018


`timescale 1ns/10ps

module NXORPD (
    clkREF,
    clkFB,
    LOCK
);
// Negated XOR Phase Detector
// I/O:
//     clkREF (bool; in): Ref clock
//     clkFB (bool; in): Compere clock
//     LOCK (bool; out): Negated XOR (LOCK) result

input clkREF;
input clkFB;
output LOCK;
wire LOCK;





assign LOCK = (!(clkREF ^ clkFB));

endmodule

Sequntial Phase Detector

The Sequential PD is the most common digital phase detector around and while there are variations of this architecture that can be found they all based on this simple but ingenious architecture. The architecture consists of two DFF where unlike in a typical state machine where a master clock control the flipping of the DFFs and the Data line into the DFFs is part of the State machine feedback loop. Here the DFFs are each on an independent clock where here the upper DFF is tied to the Reference Clock and the lower one is tied to the Feedback Clock. Therefore each one will output a high signal at a rate determined by the frequency of there respective clocks.

But that is not the end to the cleverness of this topology. The outputs of the DFFs are continuously compared by an AND gate such that only when the DFFs are in sync ($\omega_{\text{REF}}=\omega_{\text{FB}}$) will a very brief high spike will show up on both Next state lines of the DFF before the two DFFs are reset. For the other two conditions possible, only one of the two lines will have a high-value present. If $\omega_{\text{REF}}>\omega_{\text{FB}}$ then the lower output will be zero. And conversely if $\omega_{\text{REF}}<\omega_{\text{FB}}$ the upper output will be zero.

The above conditions as described by Razavi yield the following state machine for the Sequential Phase Detector

Note that the actual phase detection is the average of the two output lines. Wich not shown here, where one method to find the average is by scaling the boolean outputs to digital words and then pass the resultant words to an average and then to a low pass filter in order to implement the PLL.

Seq_PD myHDL implementation


In [10]:
@block
def SeqPD(clkREF, clkFB, UpOut, DownOut):
    """
    Sequential DFF Phase Detector
    I/O:
        clkREF (bool; in): Ref clock to Upper DFF
        clkFB (bool; in): Compare clock to Lower DFF
        UpOut (bool; ouput): Upper DFF ouput
        DownOut (bool; output): Lower DFF ouput
        
    """
    
    #and clear internal feedback sig
    clr = ResetSignal(0, active=0, async=True)

    
    #upper DFF
    @always(clkREF.posedge, clr.posedge)
    def UpD():
        if clr:
            UpOut.next=0
        else:
            UpOut.next=1
    
    #lower DFF
    @always(clkFB.posedge, clr.posedge)
    def DownD():
        if clr:
            DownOut.next=0
        else:
            DownOut.next=1
    
    #and clear
    @always_comb
    def clrLogic():
        clr.next= UpOut and DownOut
    
    return instances()

Seq_PD myHDL testing


In [11]:
#create the test signals
Peeker.clear()
clkREF=Signal(bool(0)); Peeker(clkREF, 'clkREF')
clkFB=Signal(bool(0)); Peeker(clkFB, 'clkFB')
UpOut=Signal(bool(0)); Peeker(UpOut, 'UpOut')
DownOut=Signal(bool(0)); Peeker(DownOut, 'DownOut')

#bind the test signals to the DUT
DUT=SeqPD(clkREF, clkFB, UpOut, DownOut)

def SeqPD_TB(RefDelay, FBDelay):
    """
    Test bench for Sequantial Testbench
    Args:
        RefDelay (int; 2): refrance clock delay cyles 
        FBDelay (int; 4): feedback clock delay cyles
    """
   
    #refrance clock
    @always(delay(RefDelay))
    def RefClkGen():
        clkREF.next=not clkREF
    
    #feedback clock
    @always(delay(FBDelay))
    def FBClkGen():
        clkFB.next=not clkFB
    
    #run the simulation
    @instance
    def stimulus():
        for i in range(50):
            yield clkREF.posedge
        
        raise StopSimulation()
    
    return instances()

Test when $\omega_{\text{REF}}>\omega_{\text{FB}}$


In [12]:
#create the test signals
Peeker.clear()
clkREF=Signal(bool(0)); Peeker(clkREF, 'clkREF')
clkFB=Signal(bool(0)); Peeker(clkFB, 'clkFB')
UpOut=Signal(bool(0)); Peeker(UpOut, 'UpOut')
DownOut=Signal(bool(0)); Peeker(DownOut, 'DownOut')

#bind the test signals to the DUT
DUT=SeqPD(clkREF, clkFB, UpOut, DownOut)

sim=Simulation(DUT, SeqPD_TB(3, 2), *Peeker.instances()).run()
Peeker.to_wavedrom(start_time=0, stop_time=20)


Test when $\omega_{\text{REF}}=\omega_{\text{FB}}$


In [13]:
#create the test signals
Peeker.clear()
clkREF=Signal(bool(0)); Peeker(clkREF, 'clkREF')
clkFB=Signal(bool(0)); Peeker(clkFB, 'clkFB')
UpOut=Signal(bool(0)); Peeker(UpOut, 'UpOut')
DownOut=Signal(bool(0)); Peeker(DownOut, 'DownOut')

#bind the test signals to the DUT
DUT=SeqPD(clkREF, clkFB, UpOut, DownOut)

sim=Simulation(DUT, SeqPD_TB(1, 1), *Peeker.instances()).run()
Peeker.to_wavedrom(start_time=0, stop_time=20)


Test when $\omega_{\text{REF}}<\omega_{\text{FB}}$


In [14]:
#create the test signals
Peeker.clear()
clkREF=Signal(bool(0)); Peeker(clkREF, 'clkREF')
clkFB=Signal(bool(0)); Peeker(clkFB, 'clkFB')
UpOut=Signal(bool(0)); Peeker(UpOut, 'UpOut')
DownOut=Signal(bool(0)); Peeker(DownOut, 'DownOut')

#bind the test signals to the DUT
DUT=SeqPD(clkREF, clkFB, UpOut, DownOut)

sim=Simulation(DUT, SeqPD_TB(2, 3), *Peeker.instances()).run()
Peeker.to_wavedrom(start_time=0, stop_time=20)


Verilog implementation


In [15]:
DUT.convert()
VerilogTextReader('SeqPD');


***Verilog modual from SeqPD.v***

 // File: SeqPD.v
// Generated by MyHDL 0.10
// Date: Mon May 21 21:38:27 2018


`timescale 1ns/10ps

module SeqPD (
    clkREF,
    clkFB,
    UpOut,
    DownOut
);
// Sequential DFF Phase Detector
// I/O:
//     clkREF (bool; in): Ref clock to Upper DFF
//     clkFB (bool; in): Compare clock to Lower DFF
//     UpOut (bool; ouput): Upper DFF ouput
//     DownOut (bool; output): Lower DFF ouput
//     

input clkREF;
input clkFB;
output UpOut;
reg UpOut;
output DownOut;
reg DownOut;

wire clr;



always @(posedge clkREF, posedge clr) begin: SEQPD_UPD
    if (clr) begin
        UpOut <= 0;
    end
    else begin
        UpOut <= 1;
    end
end


always @(posedge clkFB, posedge clr) begin: SEQPD_DOWND
    if (clr) begin
        DownOut <= 0;
    end
    else begin
        DownOut <= 1;
    end
end



assign clr = (UpOut && DownOut);

endmodule

/home/iridium/anaconda3/lib/python3.6/site-packages/myhdl/conversion/_toVerilog.py:296: ToVerilogWarning: Output port is read internally: UpOut
  category=ToVerilogWarning
/home/iridium/anaconda3/lib/python3.6/site-packages/myhdl/conversion/_toVerilog.py:296: ToVerilogWarning: Output port is read internally: DownOut
  category=ToVerilogWarning

Fractional Frequency Dividers

Frequency Dividers (more properly called fractional frequency dividers) are and are not a type of counter. In a traditional counter the counter would be run off a master clock to increment the counter and when the specified count is reached an indication signal would be given while also resetting the counter. In a fractional frequency divider, we can increment a counter but the incrimination is run off the input clock which may or may not be the master clock and the output of the counter reaching its count would be a then the new divided output clock. In addition, we can create "fractional" dividers such as $2/3$ via cascading a $1/2$ and a $1/3$ and modulating between them from another clock source to control the modulation.

The reason that "fractional" is in quotes in regards to $2/3$ is that the frequency divider is not actually dividing by $2/3$ but is instead switching between a $1/2$ and a $1/3$ divider. Thus $2/3$ divider is a notational misnomer. But by cascading fixed and variable dividers with counters to control the modulation based on the clock being cascaded from large fractional division can be optioned

So as stated frequency dividers are and are not a counter through some of the more advanced programmable frequency dividers such as http://tremaineconsultinggroup.com/fractional-divider-in-verilog/ (source code: https://bitbucket.org/BrianTremaine/fractional_divide/src/a68c67979c80a453d4f7dfd82f5bf90d604393f1/hardware/frac_divider.v?at=master&fileviewer=file-view-default) ) are much more like counters then the primitive ones that will be discussed here

Divide by 2

Divide by 2 myHDL implementation


In [16]:
@block
def Div2FD(clkIN, clkOUT, rst):
    """
    1/2 fractial freancy divider
    I/O:
        clkIN (input bool): input clock signal
        clkOUT (ouput bool): 1/2 ouput clock signal
        rst (input bool): reset signal
    """
    W12=Signal(bool(0))
    
    @always(clkIN.posedge)
    def D1():
        if rst:
            W12.next=0
        else:
            W12.next=clkOUT
    
    @always(clkIN.posedge)
    def D2():
        if rst:
            clkOUT.next=0
        else:
            clkOUT.next= not W12
    
    return instances()

Divide by 2 myHDL testing


In [17]:
Peeker.clear()
clkIN=Signal(bool(0)); Peeker(clkIN, 'clkIN')
clkOUT=Signal(bool(0)); Peeker(clkOUT, 'clkOUT')
rst=Signal(bool(0)); Peeker(rst, 'rst')

DUT=Div2FD(clkIN, clkOUT, rst)

def Div2FD_TB():
    
    #input clock source
    @always(delay(1))
    def ClkGen():
        clkIN.next=not clkIN
        
    #run the simulation
    @instance
    def stimulus():
        for i in range(21):
            yield clkIN.posedge
        
        raise StopSimulation()
        
    return instances()

sim=Simulation(DUT, Div2FD_TB(), *Peeker.instances()).run()

In [18]:
Peeker.to_wavedrom(start_time=0, stop_time=21)


Verilog implementation


In [19]:
DUT.convert()
VerilogTextReader('Div2FD');


***Verilog modual from Div2FD.v***

 // File: Div2FD.v
// Generated by MyHDL 0.10
// Date: Mon May 21 21:38:29 2018


`timescale 1ns/10ps

module Div2FD (
    clkIN,
    clkOUT,
    rst
);
// 1/2 fractial freancy divider
// I/O:
//     clkIN (input bool): input clock signal
//     clkOUT (ouput bool): 1/2 ouput clock signal
//     rst (input bool): reset signal

input clkIN;
output clkOUT;
reg clkOUT;
input rst;

reg W12;



always @(posedge clkIN) begin: DIV2FD_D1
    if (rst) begin
        W12 <= 0;
    end
    else begin
        W12 <= clkOUT;
    end
end


always @(posedge clkIN) begin: DIV2FD_D2
    if (rst) begin
        clkOUT <= 0;
    end
    else begin
        clkOUT <= (!W12);
    end
end

endmodule

/home/iridium/anaconda3/lib/python3.6/site-packages/myhdl/conversion/_toVerilog.py:296: ToVerilogWarning: Output port is read internally: clkOUT
  category=ToVerilogWarning

Divide by 3

Divide by 3 myHDL implementation


In [20]:
@block
def Div3FD(clkIN, clkOUT, rst):
    """
    1/3 fractial freancy divider
    I/O:
        clkIN (input bool): input clock signal
        clkOUT (ouput bool): 1/2 ouput clock signal
        rst (input bool): reset signal
    """
    W1A, WA2=[Signal(bool(0)) for _ in range(2)]
    
    @always(clkIN.posedge)
    def D1():
        if rst:
            W1A.next=0
        else:
            W1A.next=clkOUT
    
    @always(clkIN.posedge)
    def D2():
        if rst:
            clkOUT.next=0
        else:
            clkOUT.next= not WA2
    
    @always_comb
    def And():
        WA2.next=W1A and clkOUT
    
    return instances()

Divide by 3 myHDL testing


In [21]:
Peeker.clear()
clkIN=Signal(bool(0)); Peeker(clkIN, 'clkIN')
clkOUT=Signal(bool(0)); Peeker(clkOUT, 'clkOUT')
rst=Signal(bool(0)); Peeker(rst, 'rst')

DUT=Div3FD(clkIN, clkOUT, rst)

def Div3FD_TB():
    
    #input clock source
    @always(delay(1))
    def ClkGen():
        clkIN.next=not clkIN
        
    #run the simulation
    @instance
    def stimulus():
        for i in range(31):
            yield clkIN.posedge
        
        raise StopSimulation()
        
    return instances()

sim=Simulation(DUT, Div3FD_TB(), *Peeker.instances()).run()

In [22]:
Peeker.to_wavedrom(start_time=0, stop_time=19)


Verilog implementation


In [23]:
DUT.convert()
VerilogTextReader('Div3FD');


***Verilog modual from Div3FD.v***

 // File: Div3FD.v
// Generated by MyHDL 0.10
// Date: Mon May 21 21:38:31 2018


`timescale 1ns/10ps

module Div3FD (
    clkIN,
    clkOUT,
    rst
);
// 1/3 fractial freancy divider
// I/O:
//     clkIN (input bool): input clock signal
//     clkOUT (ouput bool): 1/2 ouput clock signal
//     rst (input bool): reset signal

input clkIN;
output clkOUT;
reg clkOUT;
input rst;

wire WA2;
reg W1A;



always @(posedge clkIN) begin: DIV3FD_D1
    if (rst) begin
        W1A <= 0;
    end
    else begin
        W1A <= clkOUT;
    end
end


always @(posedge clkIN) begin: DIV3FD_D2
    if (rst) begin
        clkOUT <= 0;
    end
    else begin
        clkOUT <= (!WA2);
    end
end



assign WA2 = (W1A && clkOUT);

endmodule

/home/iridium/anaconda3/lib/python3.6/site-packages/myhdl/conversion/_toVerilog.py:296: ToVerilogWarning: Output port is read internally: clkOUT
  category=ToVerilogWarning

Divide by 2/3

This is a simple hybridization of the $1/2$ and the $1/3$ frequency divider that is modulated between the two by a ModControl Signal at the or gate to create a $2/3$ frequency divider

Divide by 2/3 myHDL implementation


In [24]:
@block
def Div23FD(clkIN, ModControl, clkOUT, rst):
    """
    2/3 fractial freancy divider
    I/O:
        clkIN (input bool): input clock signal
        ModControl (input bool): modulation switching signal
        low is 1/3 high is 1/2
        clkOUT (ouput bool): 1/2 ouput clock signal
        rst (input bool): reset signal
    """
    W1O, WOA, WA2=[Signal(bool(0)) for _ in range(3)]
    
    @always(clkIN.posedge)
    def D1():
        if rst:
            W1O.next=0
        else:
            W1O.next=clkOUT
    
    @always(clkIN.posedge)
    def D2():
        if rst:
            clkOUT.next=0
        else:
            clkOUT.next=not WA2
    
    @always_comb
    def OR():
        WOA.next=W1O or ModControl
    
    @always_comb
    def AND():
        WA2.next=clkOUT and WOA
    
    
    return instances()

Divide by 2/3 myHDL testing


In [25]:
Peeker.clear()
clkIN=Signal(bool(0)); Peeker(clkIN, 'clkIN')
ModControl=Signal(bool(1)); Peeker(ModControl, 'ModControl')
clkOUT=Signal(bool(0)); Peeker(clkOUT, 'clkOUT')
rst=Signal(bool(0)); Peeker(rst, 'rst')

DUT=Div23FD(clkIN, ModControl, clkOUT, rst)

def Div23FD_TB():
    
    #input clock source
    @always(delay(1))
    def ClkGen():
        clkIN.next=not clkIN
        
    #run the simulation
    @instance
    def stimulus():
        for i in range(31):
            yield clkIN.posedge
            if i>4:
                ModControl.next=0
        
        raise StopSimulation()
        
    return instances()

sim=Simulation(DUT, Div23FD_TB(), *Peeker.instances()).run()

In [26]:
Peeker.to_wavedrom(start_time=0, stop_time=8)



In [27]:
Peeker.to_wavedrom(start_time=10, stop_time=23)



In [28]:
Peeker.to_wavedrom(start_time=0, stop_time=23)


Verilog implementation


In [29]:
DUT.convert()
VerilogTextReader('Div23SFD');


***Verilog modual from Div23SFD.v***

 // File: Div23SFD.v
// Generated by MyHDL 0.10
// Date: Mon May 21 21:02:56 2018


`timescale 1ns/10ps

module Div23SFD (
    clkIN,
    ModControl,
    clkOUT,
    rst
);
// 2/3 fractial freancy divider
// I/O:
//     clkIN (input bool): input clock signal
//     ModControl (input bool): modulation switching signal
//     low is 1/3 high is 1/2
//     clkOUT (ouput bool): 1/2 ouput clock signal
//     rst (input bool): reset signal

input clkIN;
input ModControl;
output clkOUT;
reg clkOUT;
input rst;

wire WOA;
wire WA2;
reg W1O;



always @(posedge clkIN) begin: DIV23SFD_D1
    if (rst) begin
        W1O <= 0;
    end
    else begin
        W1O <= clkOUT;
    end
end


always @(posedge clkIN) begin: DIV23SFD_D2
    if (rst) begin
        clkOUT <= 0;
    end
    else begin
        clkOUT <= (!WA2);
    end
end



assign WOA = (W1O || ModControl);



assign WA2 = (clkOUT && WOA);

endmodule

/home/iridium/anaconda3/lib/python3.6/site-packages/myhdl/conversion/_toVerilog.py:296: ToVerilogWarning: Output port is read internally: clkOUT
  category=ToVerilogWarning

Divide by 4/5

Divide by 4/5 myHDL implementation


In [30]:
@block
def Div45FD(clkIN, ModControl, clkOUT, rst):
    """
    4/5 fractial freancy divider
    I/O:
        clkIN (input bool): input clock signal
        ModControl (input bool): modulation switching signal
        low is 1/4 high is 1/5
        clkOUT (ouput bool): 1/2 ouput clock signal
        rst (input bool): reset signal
    """
    WNA11, W12, W2NA1, W2NA2, WNA23, W3NA1=[Signal(bool(0)) for _ in range(6)]
    
    
    @always_comb
    def NAND1():
        WNA11.next=not(W2NA1 and W3NA1 )
        

    @always(clkIN.posedge)
    def D1():
        if rst:
            W12.next=0
            clkOUT.next=0
        else:
            W12.next=WNA11
            clkOUT.next= WNA11
            
    @always(clkIN.posedge)
    def D2():
        if rst:
            W2NA1.next=0
            W2NA2.next=0
        else:
            W2NA1.next=W12
            W2NA2.next=not W12
    
    @always_comb
    def NAND2():
        WNA23.next=not(W2NA2 and ModControl)
    
    
    
    @always(clkIN.posedge)
    def D3():
        if rst:
            W3NA1.next=0
        else:
            W3NA1.next=WNA23
    
    
    
    
    return instances()

Divide by 4/5 myHDL testing


In [31]:
Peeker.clear()
clkIN=Signal(bool(0)); Peeker(clkIN, 'clkIN')
ModControl=Signal(bool(0)); Peeker(ModControl, 'ModControl')
clkOUT=Signal(bool(0)); Peeker(clkOUT, 'clkOUT')
rst=Signal(bool(0)); Peeker(rst, 'rst')

DUT=Div45FD(clkIN, ModControl, clkOUT, rst)

def Div45FD_TB():
    
    #input clock source
    @always(delay(1))
    def ClkGen():
        clkIN.next=not clkIN
        
    #run the simulation
    @instance
    def stimulus():
        for i in range(40):
            yield clkIN.posedge
            if i>12:
                ModControl.next=1
        
        raise StopSimulation()
        
    return instances()

sim=Simulation(DUT, Div45FD_TB(), *Peeker.instances()).run()

In [32]:
Peeker.to_wavedrom(start_time=0, stop_time=20)



In [33]:
Peeker.to_wavedrom(start_time=20, stop_time=40)



In [34]:
Peeker.to_wavedrom(start_time=0, stop_time=40)


Verilog implementation


In [35]:
DUT.convert()
VerilogTextReader('Div45FD');


***Verilog modual from Div45FD.v***

 // File: Div45FD.v
// Generated by MyHDL 0.10
// Date: Mon May 21 21:38:35 2018


`timescale 1ns/10ps

module Div45FD (
    clkIN,
    ModControl,
    clkOUT,
    rst
);
// 4/5 fractial freancy divider
// I/O:
//     clkIN (input bool): input clock signal
//     ModControl (input bool): modulation switching signal
//     low is 1/4 high is 1/5
//     clkOUT (ouput bool): 1/2 ouput clock signal
//     rst (input bool): reset signal

input clkIN;
input ModControl;
output clkOUT;
reg clkOUT;
input rst;

wire WNA23;
wire WNA11;
reg W3NA1;
reg W2NA2;
reg W2NA1;
reg W12;




assign WNA11 = (!(W2NA1 && W3NA1));


always @(posedge clkIN) begin: DIV45FD_D1
    if (rst) begin
        W12 <= 0;
        clkOUT <= 0;
    end
    else begin
        W12 <= WNA11;
        clkOUT <= WNA11;
    end
end


always @(posedge clkIN) begin: DIV45FD_D2
    if (rst) begin
        W2NA1 <= 0;
        W2NA2 <= 0;
    end
    else begin
        W2NA1 <= W12;
        W2NA2 <= (!W12);
    end
end



assign WNA23 = (!(W2NA2 && ModControl));


always @(posedge clkIN) begin: DIV45FD_D3
    if (rst) begin
        W3NA1 <= 0;
    end
    else begin
        W3NA1 <= WNA23;
    end
end

endmodule

Divide by 6 via cascaded 2 and 3

Divide by 6 myHDL implementation


In [36]:
@block
def Div6FD(clkIN, clkOUT, rst):
    """
    1/6 fractial freancy divider via cascaded 2 and 3 dividers
    using two `Div23SFD`
    I/O:
        clkIN (input bool): input clock signal
        clkOUT (ouput bool): 1/6 ouput clock signal
        rst (input bool): reset signal
    """
    clkMid=Signal(bool(0))
    MC2=Signal(bool(1));  MC3=Signal(bool(0))
    
    D2=Div23FD(clkIN, MC2, clkMid, rst)
    D3=Div23FD(clkMid, MC3, clkOUT, rst)
    
    return instances()

Divide by 6 myHDL testing


In [37]:
Peeker.clear()
clkIN=Signal(bool(0)); Peeker(clkIN, 'clkIN')
clkOUT=Signal(bool(0)); Peeker(clkOUT, 'clkOUT')
rst=Signal(bool(0)); Peeker(rst, 'rst')

DUT=Div6FD(clkIN, clkOUT, rst)

def Div6FD_TB():
    
    #input clock source
    @always(delay(1))
    def ClkGen():
        clkIN.next=not clkIN
        
    #run the simulation
    @instance
    def stimulus():
        for i in range(31):
            yield clkIN.posedge
        
        raise StopSimulation()
        
    return instances()

sim=Simulation(DUT, Div6FD_TB(), *Peeker.instances()).run()

In [38]:
Peeker.to_wavedrom(start_time=0, stop_time=21)


Verilog implementation


In [39]:
DUT.convert()
VerilogTextReader('Div6FD');


***Verilog modual from Div6FD.v***

 // File: Div6FD.v
// Generated by MyHDL 0.10
// Date: Mon May 21 21:38:37 2018


`timescale 1ns/10ps

module Div6FD (
    clkIN,
    clkOUT,
    rst
);
// 1/6 fractial freancy divider via cascaded 2 and 3 dividers
// using two `Div23SFD`
// I/O:
//     clkIN (input bool): input clock signal
//     clkOUT (ouput bool): 1/6 ouput clock signal
//     rst (input bool): reset signal

input clkIN;
output clkOUT;
reg clkOUT;
input rst;

wire MC3;
wire MC2;
reg clkMid;
wire Div23FD0_0_WOA;
wire Div23FD0_0_WA2;
reg Div23FD0_0_W1O;
wire Div23FD1_WOA;
wire Div23FD1_WA2;
reg Div23FD1_W1O;

assign MC3 = 1'd0;
assign MC2 = 1'd1;


always @(posedge clkIN) begin: DIV6FD_DIV23FD0_0_D1
    if (rst) begin
        Div23FD0_0_W1O <= 0;
    end
    else begin
        Div23FD0_0_W1O <= clkMid;
    end
end


always @(posedge clkIN) begin: DIV6FD_DIV23FD0_0_D2
    if (rst) begin
        clkMid <= 0;
    end
    else begin
        clkMid <= (!Div23FD0_0_WA2);
    end
end



assign Div23FD0_0_WOA = (Div23FD0_0_W1O || MC2);



assign Div23FD0_0_WA2 = (clkMid && Div23FD0_0_WOA);


always @(posedge clkMid) begin: DIV6FD_DIV23FD1_D1
    if (rst) begin
        Div23FD1_W1O <= 0;
    end
    else begin
        Div23FD1_W1O <= clkOUT;
    end
end


always @(posedge clkMid) begin: DIV6FD_DIV23FD1_D2
    if (rst) begin
        clkOUT <= 0;
    end
    else begin
        clkOUT <= (!Div23FD1_WA2);
    end
end



assign Div23FD1_WOA = (Div23FD1_W1O || MC3);



assign Div23FD1_WA2 = (clkOUT && Div23FD1_WOA);

endmodule

/home/iridium/anaconda3/lib/python3.6/site-packages/myhdl/conversion/_toVerilog.py:296: ToVerilogWarning: Output port is read internally: clkOUT
  category=ToVerilogWarning
/home/iridium/anaconda3/lib/python3.6/site-packages/myhdl/conversion/_toVerilog.py:349: ToVerilogWarning: Signal is not driven: MC3
  category=ToVerilogWarning
/home/iridium/anaconda3/lib/python3.6/site-packages/myhdl/conversion/_toVerilog.py:349: ToVerilogWarning: Signal is not driven: MC2
  category=ToVerilogWarning

ToDo

  1. Need to show more complicated cascades to get higher order dividers